﻿using System;
using System.Data.SqlClient;
using System.Net.NetworkInformation;
using System.Windows.Forms;
using E4RPTools;

namespace E4RPLicense
{
    public static class License
    {
        static readonly DateTime TimeBomb = new DateTime(2014, 1, 1);

        #region Private Methods

        private static DateTime GetLicenseDate(string licenseKey)
        {
            if (licenseKey.Length < 8)
                return DateTime.MinValue;
            int day;
            if (!int.TryParse(licenseKey.Substring(0, 2), out day))
                return DateTime.MinValue;
            int month;
            if (!int.TryParse(licenseKey.Substring(2, 2), out month))
                return DateTime.MinValue;
            int year;
            if (!int.TryParse(licenseKey.Substring(4, 4), out year))
                return DateTime.MinValue;
            DateTime date;
            return !DateTime.TryParse(licenseKey.Substring(0, 2) + "-" + licenseKey.Substring(2, 2) + "-" + licenseKey.Substring(4, 4), out date)
                    ? DateTime.MinValue
                    : date;
        }

        private static bool IsValidAdapterName(string adapterName)
        {
            if (adapterName == "server local area connection")
                return true;
            if (adapterName == "local area connection")
                return true;
            if (adapterName == "ligação de área local")
                return true;
            if (adapterName == "ligação de rede local do servidor")
                return true;
            if (adapterName == "zecafil")
                return true;
            return false;
        }

        public static int GetInfo()
        {
            var nics = NetworkInterface.GetAllNetworkInterfaces();
            foreach (var adapter in nics)
            {
                if (adapter.NetworkInterfaceType != NetworkInterfaceType.Ethernet)
                    continue;
                if (!IsValidAdapterName(adapter.Name.ToLower()))
                    continue;
                var address = adapter.GetPhysicalAddress();
                return address.GetHashCode();
            }
            return 0;
        }

        private static string CheckDigit(long value)
        {
            var stringValue = Math.Abs(value) + "00";
            double total = 0;
            for (var i = stringValue.Length; i > 0; i--)
            {
                var weight = Math.Pow(10, i - 1) % 97;
                var indexValue = Convert.ToInt64(stringValue.Substring(stringValue.Length - i, 1));
                total += weight * indexValue;
            }
            var result = 98 - (total % 97);
            return result.ToString();
        }

        private static string BuildLicense(int info, long productCode, long productVersion, DateTime dateTime)
        {
            var date = dateTime.Day.ToString().PadLeft(2, '0') + dateTime.Month.ToString().PadLeft(2, '0') + dateTime.Year;
            var value = Convert.ToInt64(date) + info + productCode + productVersion;
            return date + value + CheckDigit(value);
        }

        private static bool ValidateInternal(int info, long productCode, long productVersion, string licenseKey, string product, DateTime dateTimeNow)
        {
            DateTime dateTime = DateTime.Now;
            if(dateTimeNow.Date != dateTime.Date)
            {
                MessageBox.Show("Licença inválida." + Environment.NewLine + "A data do servidor é diferente da data do computador.", product, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }
            if (!IsLicenceValid(licenseKey, info, productCode, productVersion, dateTimeNow))
            {
                MessageBox.Show("Licença inválida." + Environment.NewLine + string.Format("Indique o código '{0}' ao fornecedor, de modo a obter a licença.", info),
                            product, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }
            LicenceDateExpiring(licenseKey, info, product, dateTimeNow);
            return true;
        }

        private static bool IsLicenceValid(string licenseKey, int info, long productCode, long productVersion, DateTime dateTimeNow)
        {
            if (licenseKey.ToLower() == "demo" && dateTimeNow < TimeBomb)
                return true;
            var date = GetLicenseDate(licenseKey);
            if (date == DateTime.MinValue || date.Date < dateTimeNow.Date)
                return false;
            return BuildLicense(info, productCode, productVersion, date) == licenseKey;
        }

        private static void LicenceDateExpiring(string licenseKey, int info, string product, DateTime dateTimeNow)
        {
            if (licenseKey.ToLower() == "demo" && dateTimeNow < TimeBomb)
                return;
            var date = GetLicenseDate(licenseKey);
            TimeSpan timeSpan = date.Date.Subtract(dateTimeNow);
            if (timeSpan.Days < 31)
                MessageBox.Show(string.Format("Licença válida por {0} dias.", timeSpan.Days) + Environment.NewLine + string.Format("Indique o código '{0}' ao fornecedor, de modo a obter a licença.", info),
                            product, MessageBoxButtons.OK, MessageBoxIcon.Warning);            
        }

        private static string GetLicenseKey(SqlConnection sqlConnection, int info, IWin32Window ownerWindow, string logFile, string product)
        {
            var sqlTransaction = sqlConnection.BeginTransaction();
            try
            {
                string license;
                const string query =
                    @"  select	KeyValue
                        from    E4RPLicense
                        where   info = @Info";
                using (var dataAccess = new SqlDataAccess())
                    license = dataAccess.ExecuteScalar(sqlTransaction, "", query, new SqlParameter("Info", info));
                sqlTransaction.Commit();
                return license;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(ownerWindow, e, logFile, product, string.Format("Não foi possível ler a licença dado o info '{0}'.", info));
                return "";
            }
        }

        private static DateTime GetDateTimeNow(SqlConnection sqlConnection, IWin32Window ownerWindow, string logFile, string product)
        {
            var sqlTransaction = sqlConnection.BeginTransaction();
            try
            {
                DateTime dateTimeNow;
                const string query = "select convert( datetime, convert(varchar, getdate(), 104), 104)";
                using (var dataAccess = new SqlDataAccess())
                    dateTimeNow = dataAccess.ExecuteScalar(sqlTransaction, DateTime.MinValue, query);
                sqlTransaction.Commit();
                return dateTimeNow;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(ownerWindow, e, logFile, product, string.Format("Não foi possível ler a data actual."));
                return DateTime.MinValue;
            }
        }

        #endregion


        public static bool Validate(string product, long productCode, long productVersion, string licenseKey)
        {
            var info = GetInfo();
            return ValidateInternal(info, productCode, productVersion, licenseKey, product, DateTime.Now);
        }


        public static bool Validate(Product product, long productVersion, string licenseKey)
        {
            var info = GetInfo();
            return ValidateInternal(info, (long)product, productVersion, licenseKey, product.ToString(), DateTime.Now);
        }
        

        public static bool ValidateDatabase(SqlConnection sqlConnection, Product product, long productVersion, IWin32Window ownerWindow, string logFile)
        {
            var info = GetInfo();
            string licenseKey = GetLicenseKey(sqlConnection, info, ownerWindow, logFile, product.ToString());
            DateTime dateTimeNow = GetDateTimeNow(sqlConnection, ownerWindow, logFile, product.ToString());
            return ValidateInternal(info, (long)product, productVersion, licenseKey, product.ToString(), dateTimeNow);
        }
    }
}
